package han.cirno.corrupt.components.school.impl.mju

import java.math.BigInteger
import java.util.*
import kotlin.math.ceil

object MjuRSAUtil {
    fun encrypt(modulusHex: String, exponentHex: String, data: ByteArray): String {
        val modulus = BigInteger("00$modulusHex", 16)
        val exponent = BigInteger(exponentHex, 16)
        val chunkSize = getChunkSize(modulusHex)
        val paddedData = paddingData(data, chunkSize)
        val stringBuilder = StringBuilder()
        for (i in paddedData.indices step chunkSize) {
            val block = JSBigInt()
            var j = 0
            var k = i
            while (k < i + chunkSize) {
                block.digits[j] = paddedData[k++].toShort()
                block.digits[j] = (block.digits[j] + (paddedData[k++].toInt() shl 8).toShort()).toShort()
                j++
            }
            val crypt = block.toJavaBigInteger().modPow(exponent, modulus)
            stringBuilder.append(toHex(crypt.toByteArray()))
        }
        return stringBuilder.toString()
    }
    private fun paddingData(data: ByteArray, chunkSize: Int): ByteArray {
        val newData = ByteArray(ceil(data.size / chunkSize.toDouble()).toInt() * chunkSize)
        Arrays.fill(newData, 0.toByte())
        System.arraycopy(data, 0, newData, 0, data.size)
        return newData
    }
    private fun toHex(bytes: ByteArray): String {
        val sb = StringBuilder(bytes.size * 2)
        for (b in bytes) sb.append(String.format("%02x", b))
        return sb.toString()
    }
    private fun getChunkSize(modulusHex: String): Int {
        val o = ceil(modulusHex.length / 4.0).toInt()
        return (o - 1) * 2
    }
    class JSBigInt {
        var digits: ShortArray = ShortArray(130)
        init { Arrays.fill(digits, 0.toShort()) }
        fun toJavaBigInteger(): BigInteger {
            val hexString = StringBuilder()
            for (digit in digits) hexString.insert(0, String.format("%02x", digit))
            return BigInteger(hexString.toString(), 16)
        }
    }
}
